using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using ThouShaltNotBrick.Logic;
using ThouShaltNotBrick.Logic.GameStates;
using ThouShaltNotBrick.Logic.Resources;
using GameUtils.Input;
using GMLogging;
using GameUtils.GameManager;
using GameUtils.Resources;

namespace ThouShaltNotBrick
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public sealed class TsnbGame : Game, IGameManager
    {
        #region Properties
        private GraphicsDeviceManager mGraphics;
        private Dictionary<Type, IResourceContainer> mResourceContainers;
        #endregion

        #region Events
        #region ClientSizeChanged
        public event EventHandler ClientSizeChanged;
        private void RaiseClientSizeChangedEvent()
        {
            var handler = ClientSizeChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
        #endregion
        #endregion

        #region Constructors
        public TsnbGame()
        {
            mGraphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "ThouShaltNotBrick.Content";

            mGraphics.PreferredBackBufferWidth = 960;
            mGraphics.PreferredBackBufferHeight = 600;
            mGraphics.IsFullScreen = false;
            mGraphics.SynchronizeWithVerticalRetrace = false;

            IsFixedTimeStep = false;
            IsMouseVisible = true;

            //GraphicsHelper.GetDisplayModes();
            Logger.Initialize("trace.txt");
        }
        #endregion

        #region Overriden Methods
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            mResourceContainers = new Dictionary<Type, IResourceContainer>();
            mResourceContainers.Add(typeof(GraphicsResources), new GraphicsResources());
            mResourceContainers.Add(typeof(FontResources), new FontResources());
            mResourceContainers.Add(typeof(GameResources), new GameResources());

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            RasterizerState rasterizerState = new RasterizerState();
            rasterizerState.ScissorTestEnable = true;
            mGraphics.GraphicsDevice.RasterizerState = rasterizerState;

            Window.ClientSizeChanged += ClientSizeChangedHandler;

            foreach (IResourceContainer resourceContainer in mResourceContainers.Values)
            {
                resourceContainer.Initialize(Content);
            }

            EventInput.Initialize(Window);
            EventInput.WMKeyDown += KeyDownHandler;
            EventInput.WMKeyUp += KeyUpHandler;
            EventInput.WMChar += CharHandler;
            EventInput.WMLButtonDown += LButtonDownHandler;
            EventInput.WMLButtonUp += LButtonUpHandler;
            EventInput.WMMButtonDown += MButtonDownHandler;
            EventInput.WMMButtonUp += MButtonUpHandler;
            EventInput.WMRButtonDown += RButtonDownHandler;
            EventInput.WMRButtonUp += RButtonUpHandler;
            EventInput.WMMouseMove += MouseMoveHandler;
            EventInput.WMMouseWheel += MouseWheelHandler;

            GameStateManager.Initialize(this);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            GameStateManager.Destroy();

            EventInput.WMKeyDown -= KeyDownHandler;
            EventInput.WMKeyUp -= KeyUpHandler;
            EventInput.WMChar -= CharHandler;
            EventInput.WMLButtonDown -= LButtonDownHandler;
            EventInput.WMLButtonUp -= LButtonUpHandler;
            EventInput.WMMButtonDown -= MButtonDownHandler;
            EventInput.WMMButtonUp -= MButtonUpHandler;
            EventInput.WMRButtonDown -= RButtonDownHandler;
            EventInput.WMRButtonUp -= RButtonUpHandler;
            EventInput.WMMouseMove -= MouseMoveHandler;
            EventInput.WMMouseWheel -= MouseWheelHandler;

            mResourceContainers.Clear();

            Window.ClientSizeChanged -= ClientSizeChangedHandler;
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="pGameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime pGameTime)
        {
            PollingInput.PreHandleInput();
            GameStateManager.CurrState.HandleInput(pGameTime);
            PollingInput.PostHandleInput();

            GameStateManager.CurrState.Update(pGameTime);

            base.Update(pGameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime pGameTime)
        {
            GameStateManager.CurrState.Draw(pGameTime);

            base.Draw(pGameTime);
        }

        protected override void OnExiting(object sender, EventArgs args)
        {
            Logger.Destroy();

            base.OnExiting(sender, args);
        }

        #region IGameStateManager
        public GraphicsDeviceManager GetGraphics()
        {
            return mGraphics;
        }

        public T GetResourcesContainer<T>() where T : IResourceContainer
        {
            Type type = typeof(T);
            if (mResourceContainers.ContainsKey(type))
            {
                return (T)mResourceContainers[type];
            }
            else
            {
                return default(T);
            }
        }

        public int GetClientWidth()
        {
            return Window.ClientBounds.Width;
        }

        public int GetClientHeight()
        {
            return Window.ClientBounds.Height;
        }

        public void SetMouseVisibility(bool pVisible)
        {
            IsMouseVisible = pVisible;
        }

        public void ExitGame()
        {
            Exit();
        }
        #endregion
        #endregion

        #region Event Handlers
        private void ClientSizeChangedHandler(object sender, EventArgs e)
        {
            RaiseClientSizeChangedEvent();
        }

        private void KeyDownHandler(object sender, KeyEventArgs e)
        {
            GameStateManager.CurrState.OnKeyDown(e);
        }

        private void KeyUpHandler(object sender, KeyEventArgs e)
        {
            GameStateManager.CurrState.OnKeyUp(e);
        }

        private void CharHandler(object sender, CharacterEventArgs e)
        {
            GameStateManager.CurrState.OnChar(e);
        }

        private void LButtonDownHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnLeftButtonDown(e);
        }

        private void LButtonUpHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnLeftButtonUp(e);
        }

        private void MButtonDownHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnMiddleButtonDown(e);
        }

        private void MButtonUpHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnMiddleButtonUp(e);
        }

        private void RButtonDownHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnRightButtonDown(e);
        }

        private void RButtonUpHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnRightButtonUp(e);
        }

        private void MouseMoveHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnMouseMove(e);
        }

        private void MouseWheelHandler(object sender, MouseEventArgs e)
        {
            GameStateManager.CurrState.OnMouseWheel(e);
        }
        #endregion
    }
}